package org.uni_hi.muus.server.http;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.PrintStream;

import lib.java.net.MalformedURLException;
import lib.java.net.URL;

import org.uni_hi.muus.server.util.HTTPServerCodes;
import org.uni_hi.muus.server.util.SupportCheck;
import org.uni_hi.muus.util.StringUtil;

public class Request extends Header{
	
	private HTTPStreamParser _parser=null;
	
	public static final String 
		FIELD_ACCEPT="accept",
		FIELD_ACCEPT_CHARSET="accept-charset",
		FIELD_ACCEPT_ENCODING="accept-encoding",
		FIELD_ACCEPT_LANGUAGE="accept-language",
		FIELD_AUTHORIZATION="authorization",
		FIELD_COOKIE="cookie",
		FIELD_HOST="host",
		FIELD_IF_MATCH="if-match",
		FIELD_IF_MODIFIED_SINCE="if-modified-since",
		FIELD_IF_NONE_MATCH="if-none-match",
		FIELD_IF_RANGE="if-range",
		FIELD_IF_UNMODIFIED_SINCE="if-unmodified-since",
		FIELD_MAX_FORWARDS="max-forwards",
		FIELD_PROXY_AUTHORIZATION="proxy-authorization",
		FIELD_RANGE="range",
		FIELD_REFERER="referer",
		FIELD_USER_AGENT="user-agent";

	private static final String[] REQUEST_FIELDS={
		FIELD_ACCEPT,
		FIELD_ACCEPT_CHARSET,
		FIELD_ACCEPT_ENCODING,
		FIELD_ACCEPT_LANGUAGE,
		FIELD_ACCEPT_RANGES,
		FIELD_AUTHORIZATION,
		FIELD_COOKIE,
		FIELD_HOST,
		FIELD_IF_MATCH,
		FIELD_IF_MODIFIED_SINCE,
		FIELD_IF_NONE_MATCH,
		FIELD_IF_RANGE,
		FIELD_IF_UNMODIFIED_SINCE,
		FIELD_MAX_FORWARDS,
		FIELD_PROXY_AUTHORIZATION,
		FIELD_RANGE,
		FIELD_REFERER,
		FIELD_USER_AGENT,
		FIELD_VIA,
		FIELD_WARN};
	
	protected String _method=null;
	protected URL _url=null;
	protected String _httpVersion=null;

	private boolean blockContentRead=false;

	/**
	 * Creates a new Request-object. Will start reading data from DataInputStream is and
	 * try to make sense out of the parsed data. Utilizes the HTTPStreamReader-Class. 
	 * @param is
	 * @param ps required for creating later a Response-object out of the Request-object.
	 */
	public Request(DataInputStream is,PrintStream ps){
		super(StringUtil.CRLF,": ",is,ps);
		_parser=new HTTPStreamParser(this,new DataInputStream(is));
	}
	
	public String getRequestLine(){
		return _method+" "+_url.getPath()+" "+_httpVersion+_splitToken;
	}
	
	public String getMethod(){
		return _method;
	}
	
	public URL getURL(){
		return _url;
	}
	
	public String getHTTPVersion(){
		return _httpVersion;
	}
	
	
	public synchronized void readRequest(){
		blockContentRead=true;
		readHeaderData();
		readContent();
		while(blockContentRead){
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 
	 * @param s
	 * @return
	 */
	protected void readHeaderData(){
		try {
			_parser.readDataInputStream();
		} catch (IOException e) {
			setStatusCode(HTTPServerCodes.HTTP_CODE_500,"Failed to read Header Data!");
		}
	}
	
	protected void readContent(){
		String t=(String)get(FIELD_CONTENT_LENGTH);
		if(t!=null){
			int length=Integer.parseInt(t);
			_content=new byte[length];
			try {
				_is.readFully(_content);
			} catch (IOException e) {
				setStatusCode(HTTPServerCodes.HTTP_CODE_500,"Failed to read Content Data!");
			}
		}
		unblockContentRead();
	}
	
	private synchronized void unblockContentRead(){
		blockContentRead=false;
		notifyAll();
	}
	
	/**
	 * Parses the first line of the HTTP-Request, containing METHOD,
	 * RESOURCE-PATH and HTTP-VERSION.
	 * @param s first line - without CR or LF
	 * @throws MalformedURLException
	 */
	public void readRequestLine(){
		try {
			String s = _parser.getStartLine();
			
			int endIndex=s.indexOf(" "),startIndex=0;
			_method=s.substring(startIndex, endIndex).toUpperCase();
			if(!SupportCheck.isMethodSupported(_method)){
				setStatusCode(HTTPServerCodes.HTTP_CODE_501,
						"The method used is not implemented. Please use " +
						"one of the following instead:<br>"+
						SupportCheck.getSupportedMethods());
			}
			
			endIndex=s.indexOf(" ", (startIndex=endIndex+1));
			try {
				_url=new URL("http","localhost",s.substring(startIndex, endIndex));
			} catch (MalformedURLException e) {
				e.printStackTrace();
				setStatusCode(HTTPServerCodes.HTTP_CODE_404,null);
			}
			startIndex=endIndex+1;
			endIndex=s.length();
			_httpVersion=s.substring(startIndex, endIndex);
			if(!SupportCheck.isVersionSupported(_httpVersion)){
				setStatusCode(HTTPServerCodes.HTTP_CODE_505,"Only "+HTTPServerCodes.HTTP_VERSION+" is supported.");
			}
		} catch (IOException e1) {
			setStatusCode(HTTPServerCodes.HTTP_CODE_500,"Failed to read the Request Line.");
		}
	}
	
	public String toString(){
		return (getRequestLine()+printFields(REQUEST_FIELDS)+super.toString());
	}
}
